﻿using Base.Common.Events;
using Microsoft.Win32;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Management;
using System.Windows;
using System.Windows.Interop;
using Prism.Ioc;

namespace System
{
    /// <summary>
    /// USB工具类
    /// </summary>
    public sealed class UsbHelper
    {
        #region u盘属性
        private const int WM_DEVICECHANGE = 0x219;//U盘插入后，OS的底层会自动检测到，然后向应用程序发送“硬件设备状态改变“的消息
        private const int DBT_DEVICEARRIVAL = 0x8000;  //就是用来表示U盘可用的。一个设备或媒体已被插入一块，现在可用。
        private const int DBT_CONFIGCHANGECANCELED = 0x0019;  //要求更改当前的配置（或取消停靠码头）已被取消。
        private const int DBT_CONFIGCHANGED = 0x0018;  //当前的配置发生了变化，由于码头或取消固定。
        private const int DBT_CUSTOMEVENT = 0x8006; //自定义的事件发生。 的Windows NT 4.0和Windows 95：此值不支持。
        private const int DBT_DEVICEQUERYREMOVE = 0x8001;  //审批要求删除一个设备或媒体作品。任何应用程序也不能否认这一要求，并取消删除。
        private const int DBT_DEVICEQUERYREMOVEFAILED = 0x8002;  //请求删除一个设备或媒体片已被取消。
        private const int DBT_DEVICEREMOVECOMPLETE = 0x8004;  //一个设备或媒体片已被删除。
        private const int DBT_DEVICEREMOVEPENDING = 0x8003;  //一个设备或媒体一块即将被删除。不能否认的。
        private const int DBT_DEVICETYPESPECIFIC = 0x8005;  //一个设备特定事件发生。
        private const int DBT_DEVNODES_CHANGED = 0x0007;  //一种设备已被添加到或从系统中删除。
        private const int DBT_QUERYCHANGECONFIG = 0x0017;  //许可是要求改变目前的配置（码头或取消固定）。
        private const int DBT_USERDEFINED = 0xFFFF;  //此消息的含义是用户定义的
        #endregion

        /// <summary>
        /// WPF的View挂载钩子函数用的HwndSource
        /// </summary>
        private static HwndSource _hwndSource;

        /// <summary>
        /// USB状态改变事件
        /// </summary>
        private static UsbChangedEvent _usbChangedEvent;

        public static bool IsSystemUSBEnabled()
        {
            bool ret = true;
            try
            {
                RegistryKey regKey = Registry.LocalMachine; //读取注册列表HKEY_LOCAL_MACHINE  
                string keyPath = @"SYSTEM\CurrentControlSet\Services\USBSTOR"; //USB 大容量存储驱动程序  
                RegistryKey openKey = regKey.OpenSubKey(keyPath, true);
                var value = openKey.GetValue("Start").ToString();

                openKey.Close(); //关闭注册列表读写流  

                ret = (value == "3");
            }
            catch
            {
                return true;
            }

            return ret;
        }

        public static bool EnableUSB()
        {
            try
            {
                RegistryKey regKey = Registry.LocalMachine; //读取注册列表HKEY_LOCAL_MACHINE  
                string keyPath = @"SYSTEM\CurrentControlSet\Services\USBSTOR"; //USB 大容量存储驱动程序  
                RegistryKey openKey = regKey.OpenSubKey(keyPath, true);

                openKey.SetValue("Start", 3); //设置键值对（3）为开启USB（4）为关闭 
                openKey.Close(); //关闭注册列表读写流  

                return true;
            }
            catch
            {
                return false;
            }
        }

        public static bool DisableUSB()
        {
            try
            {
                RegistryKey regKey = Registry.LocalMachine; //读取注册列表HKEY_LOCAL_MACHINE  
                string keyPath = @"SYSTEM\CurrentControlSet\Services\USBSTOR"; //USB 大容量存储驱动程序  
                RegistryKey openKey = regKey.OpenSubKey(keyPath, true);

                openKey.SetValue("Start", 4); //设置键值对（3）为开启USB（4）为关闭 
                openKey.Close(); //关闭注册列表读写流  

                return true;
            }
            catch
            {
                return false;
            }
        }

        public static List<string> GetUSBVolumeNameList()
        {
            List<string> ret = new List<string>();
            ManagementObjectSearcher diskDrives = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
            foreach (ManagementObject diskDrive in diskDrives.Get())
            {
                string DeviceID = diskDrive["DeviceID"].ToString();
                string DriveLetter = "";
                string DriveDescription = "";

                // associate physical disks with partitions
                ManagementObjectSearcher partitionSearcher = new ManagementObjectSearcher(String.Format(
                    "associators of {{Win32_DiskDrive.DeviceID='{0}'}} where AssocClass = Win32_DiskDriveToDiskPartition",
                    diskDrive["DeviceID"]));

                foreach (ManagementObject partition in partitionSearcher.Get())
                {
                    // associate partitions with logical disks (drive letter volumes)
                    ManagementObjectSearcher logicalSearcher = new ManagementObjectSearcher(String.Format(
                        "associators of {{Win32_DiskPartition.DeviceID='{0}'}} where AssocClass = Win32_LogicalDiskToPartition",
                        partition["DeviceID"]));

                    foreach (ManagementObject logical in logicalSearcher.Get())
                    {
                        // finally find the logical disk entry to determine the volume name
                        ManagementObjectSearcher volumeSearcher = new ManagementObjectSearcher(String.Format(
                            "select * from Win32_LogicalDisk where Name='{0}'",
                            logical["Name"]));

                        foreach (ManagementObject volume in volumeSearcher.Get())
                        {
                            DriveLetter = volume["Name"].ToString();
                            if (volume["VolumeName"] != null)
                                DriveDescription = volume["VolumeName"].ToString();

                            char VolumeLetter = DriveLetter[0];
                            string VolumeName = DriveDescription;
                            string Manufacturer = (string)diskDrive["Manufacturer"];
                            string MediaType = (string)diskDrive["MediaType"];
                            string Model = (string)diskDrive["Model"];
                            string SerialNumber = (string)diskDrive["SerialNumber"];
                            long Size = Convert.ToInt64(volume["Size"]);
                            long FreeSpace = Convert.ToInt64(volume["FreeSpace"]);
                            if (MediaType == "External hard disk media" || MediaType == "Removable Media")
                            {
                                ret.Add(VolumeLetter.ToString());
                            }
                        }
                    }
                }
            }
            return ret;
        }

        /// <summary>
        /// 开始监听USB设备变化
        /// </summary>
        /// <returns>是否开启成功</returns>
        public static bool StartListenUsbChange()
        {
            bool isSuccess = false;

            try
            {
                AsyncActionHelper.RunDispatcherTask(() =>
                {
                    try
                    {
                        #region 挂在钩子函数
                        if (_hwndSource == null)
                        {
                            _hwndSource = PresentationSource.FromVisual(Application.Current.MainWindow) as HwndSource;
                        }

                        _hwndSource.RemoveHook(WndProc);
                        _hwndSource.AddHook(WndProc);
                        #endregion

                        #region 获取事件
                        _usbChangedEvent = ContainerLocator.Current.Resolve<IEventAggregator>().GetEvent<UsbChangedEvent>();
                        if (_usbChangedEvent == null)
                        {
                            isSuccess = false;
                            return;
                        }
                        #endregion

                        isSuccess = true;
                    }
                    catch (Exception)
                    {
                        isSuccess = false;
                    }
                },
                isAsync: false);
            }
            catch (Exception)
            {
                isSuccess = false;
            }

            return isSuccess;
        }

        /// <summary>
        /// 停止监听USB设备变化
        /// </summary>
        public static void EndListenUsbChange()
        {
            _hwndSource?.RemoveHook(WndProc);
        }

        /// <summary>
        /// WndProc
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="msg"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <param name="handled"></param>
        /// <returns></returns>
        private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_DEVICECHANGE)
            {
                switch (wParam.ToInt32())
                {
                    case DBT_DEVICEARRIVAL:
                        //U盘可用
                        _usbChangedEvent.Publish(new UsbChangedEventArgs(UsbChangeTypes.DBT_DEVICEARRIVAL));
                        break;
                    case DBT_DEVICEREMOVECOMPLETE:
                        //U盘卸载
                        _usbChangedEvent.Publish(new UsbChangedEventArgs(UsbChangeTypes.DBT_DEVICEREMOVECOMPLETE));
                        break;
                }
            }

            return IntPtr.Zero;
        }
    }
}
